/*
 * Copyright (C) 2008-2009 the original author or authors
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *         http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.code.liquidform.internal;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * A set of utility methods about JavaBeans.
 */
public final class BeanUtils {

	private static final String GET = "get";

	private static final String IS = "is";

	private BeanUtils() {
		// utility class
	}

	/**
	 * Is the passed in method a <i>getter</i> method (ie no argument, does not
	 * return <tt>void</tt> and starts with <tt>get</tt> - or <tt>is</tt> if the
	 * return type is boolean).
	 */
	public static boolean isGetter(Method method) {
		if (method.getParameterTypes().length > 0) {
			return false;
		}
		String name = method.getName();
		Class<?> returnType = method.getReturnType();
		boolean b = returnType.equals(Boolean.class)
				|| returnType.equals(Boolean.TYPE);

		return !returnType.equals(Void.class)
				&& (name.startsWith(GET) || (b && name.startsWith(IS)));
	}

	public static String propertyNameForGetter(Method getter) {
		if (getter.getParameterTypes().length > 0) {
			throw new IllegalArgumentException("Method " + getter
					+ " is not a getter because it doesn't have 0 args");
		}
		String methodName = getter.getName();
		if (methodName.startsWith(IS)
				&& (getter.getReturnType().equals(Boolean.class) || getter
						.getReturnType().equals(Boolean.TYPE))) {
			return decapitalize(methodName.substring(IS.length()));
		} else if (methodName.startsWith(GET)) {
			return decapitalize(methodName.substring(GET.length()));
		} else {
			throw new IllegalStateException("Method " + getter
					+ " does not follow JavaBeans naming conventions");
		}
	}

	public static String decapitalize(String substring) {
		if (substring.length() >= 2
				&& Character.isUpperCase(substring.charAt(0))
				&& Character.isUpperCase(substring.charAt(1))) {
			// Do not change stuff like URL
			return substring;
		} else {
			StringBuilder sb = new StringBuilder(substring.length());
			sb.append(Character.toLowerCase(substring.charAt(0))).append(
					substring.substring(1));
			return sb.toString();
		}
	}

	public static <A extends Annotation> A getPropertyOrGetterAnnotation(
			Method getter, Class<A> annotationClass) {
		if (getter.isAnnotationPresent(annotationClass)) {
			return getter.getAnnotation(annotationClass);
		}

		String propertyNameForGetter = BeanUtils.propertyNameForGetter(getter);
		try {
			Field field = getter.getDeclaringClass().getDeclaredField(
					propertyNameForGetter);
			return field.getAnnotation(annotationClass);
		} catch (NoSuchFieldException e) {
			return null;
		}
	}
}
